﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Domain.Repositories;
using Abp.IdentityFramework;
using Abp.UI;
using FwProject.Authorization;
using FwProject.Authorization.Roles;
using FwProject.Authorization.Users;
using FwProject.Roles.Dto;
using X.PagedList;
using Abp.Runtime.Caching;
using AutoMapper.QueryableExtensions;
using Abp.AutoMapper;

namespace FwProject.Roles
{
    [AbpAuthorize(PermissionNames.Pages_Roles)]
    public class RoleAppService : AsyncCrudAppService<Role, RoleDto, int, PagedResultRequestDto, CreateRoleDto, RoleDto>, IRoleAppService
    {
        private readonly IRepository<Role,int> _repositoryRole;
        private readonly RoleManager _roleManager;
        private readonly UserManager _userManager;
        private readonly ICacheManager _cacheManager;
        private readonly IPermissionManager _permissionManager;


  
        public RoleAppService(IRepository<Role> repository, RoleManager roleManager, UserManager userManager, ICacheManager cacheManager, IPermissionManager permissionManager, IRepository<Role, int> repositoryRole)
            : base(repository)
        {
            _repositoryRole = repositoryRole;
            _roleManager = roleManager;
            _userManager = userManager;
            _cacheManager = cacheManager;
            _permissionManager = permissionManager;
        }

        public override async Task<RoleDto> Create(CreateRoleDto input)
        {
            CheckCreatePermission();

            var role = ObjectMapper.Map<Role>(input);
            role.SetNormalizedName();

            CheckErrors(await _roleManager.CreateAsync(role));

            var grantedPermissions = PermissionManager
                .GetAllPermissions()
                .Where(p => input.Permissions.Contains(p.Name))
                .ToList();

            await _roleManager.SetGrantedPermissionsAsync(role, grantedPermissions);

            return MapToEntityDto(role);
        }

        public override async Task<RoleDto> Update(RoleDto input)
        {
            CheckUpdatePermission();

            var role = await _roleManager.GetRoleByIdAsync(input.Id);

            ObjectMapper.Map(input, role);

            CheckErrors(await _roleManager.UpdateAsync(role));

            var grantedPermissions = PermissionManager
                .GetAllPermissions()
                .Where(p => input.Permissions.Contains(p.Name))
                .ToList();

            await _roleManager.SetGrantedPermissionsAsync(role, grantedPermissions);

            return MapToEntityDto(role);
        }

        public override async Task Delete(EntityDto<int> input)
        {
            CheckDeletePermission();

            var role = await _roleManager.FindByIdAsync(input.Id.ToString());
            var users = await _userManager.GetUsersInRoleAsync(role.NormalizedName);

            foreach (var user in users)
            {
                CheckErrors(await _userManager.RemoveFromRoleAsync(user, role.NormalizedName));
            }

            CheckErrors(await _roleManager.DeleteAsync(role));
        }

        public Task<ListResultDto<PermissionDto>> GetAllPermissions()
        {
            var permissions = PermissionManager.GetAllPermissions();

         

            return Task.FromResult(new ListResultDto<PermissionDto>(
                ObjectMapper.Map<List<PermissionDto>>(permissions)
            ));
        }

        protected override IQueryable<Role> CreateFilteredQuery(PagedResultRequestDto input)
        {
            return Repository.GetAllIncluding(x => x.Permissions);
        }

        protected override async Task<Role> GetEntityByIdAsync(int id)
        {
            return await Repository.GetAllIncluding(x => x.Permissions).FirstOrDefaultAsync(x => x.Id == id);
        }

        protected override IQueryable<Role> ApplySorting(IQueryable<Role> query, PagedResultRequestDto input)
        {
            return query.OrderBy(r => r.DisplayName);
        }

        protected virtual void CheckErrors(IdentityResult identityResult)
        {
            identityResult.CheckErrors(LocalizationManager);
        }

        public async Task<IPagedList<RoleOutputDto>> GetPagedListAsync(int pageIdx, int pageSize)
        {
            IQueryable<Role> query = (await GetCache).AsQueryable();//_contactRepository.GetAll();
            var user =await _userManager.FindByIdAsync(AbpSession.UserId.Value.ToString());

            if (!IsGranted(PermissionNames.Factory_AdminUser_All))
                query = query.Where(o => o.FactoryinfoId == user.FactoryinfoId && o.Name != PermissionNames.Factory_AdminUser_All);


            var data = await query.ProjectTo<RoleOutputDto>().ToPagedListAsync(pageIdx, pageSize);
            return data;
        }


        public async Task<List<RoleOutputDto>> GetAllAsync(string keyword)
        {
            var query = Repository.GetAll();
            if (!string.IsNullOrWhiteSpace(keyword))
                query = query.Where(o => o.Name.Contains(keyword));

            var user = await _userManager.FindByIdAsync(AbpSession.UserId.Value.ToString());
            if (!(await IsGrantedAsync(PermissionNames.Factory_AdminUser_All)))
                query = query.Where(o => o.FactoryinfoId == user.FactoryinfoId);

            var list =await query.ProjectTo<RoleOutputDto>().ToListAsync();
            return list;
        }

        public async Task UpdatePermissionsAsync(int id, RolePermissionsUpdateDto input)
        {
            var role = await _roleManager.GetRoleByIdAsync(id);
            var grantedPermissions = _permissionManager
                .GetAllPermissions()
                .Where(p => input.GrantedPermissionNames.Contains(p.Name))
                .ToList();
         
            await _roleManager.SetGrantedPermissionsAsync(role, grantedPermissions);
        }

        public async Task<int> CreateAndGetIdAsync(RoleEnterDto input)
        {
            var position = input.MapTo<Role>();
            var user = await _userManager.FindByIdAsync(AbpSession.UserId.Value.ToString());
            position.FactoryinfoId = user.FactoryinfoId;

            var identityResult = await _roleManager.CreateAsync(position);
            CheckErrors(identityResult);
            return position.Id;
        }

        public async Task<RoleEnterDto> GetEnterEntityAsync(int id)
        {
            var entity =await _repositoryRole.GetAsync(id);
            return entity.MapTo<RoleEnterDto>();
        }

        public async Task UpdateAsync(int id, RoleEnterDto input)
        {
            var entity = await _repositoryRole.GetAsync(id);
            input.MapTo(entity);
            var check = await _roleManager.CheckDuplicateRoleNameAsync(id, input.Name, input.DisplayName);
            if (check.Succeeded)
            {
                //Microsoft.AspNet.Identity 封装的操作
                var identityResult = await _roleManager.UpdateAsync(entity);

                CheckErrors(identityResult);
            }
        }

        public async Task DeleteAsync(int id)
        {
            var entity = await _repositoryRole.GetAsync(id);
            await _roleManager.DeleteAsync(entity);
        }

        private  Task<List<Role>> GetCache
        {

            get
            {
                ICache cache = _cacheManager.GetCache(nameof(Role));
                return cache.GetAsync("list", async () => await _repositoryRole.GetAllListAsync());
            }

        }
    }
}
